/* Copyright (C) 2021 Rain */

/* This file is part of XNIX. */

/* 
  XNIX is free software: you can redistribute it and/or modify 
  it under the terms of the GNU General Public License as published by 
  the Free Software Foundation, either version 3 of the License, or 
  (at your option) and later version. 
*/

/*
  XNIX is distributed in the hope that it will be useful, 
  but WITHOUT ANY WARRANTY; without even the implied warranty of 
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
  GNU General Public License for more details. 
*/

/*
  You should have received a copy of the GNU General Public License 
   along with XNIX. If not, see <https://www.gnu.org/licenses/>.  
*/



/* #NO! it is BRIGHT+RED (well...) */
/* we are in VGA mode now */

#define RED $0x00ff0000

/* 
 * arguments in long-mode: 
 * %rdi, %rsi, %rdx, %rcx, and...
 * stack...
 */

#define WARNING(msg)	\
	movq 0x0e, %rdi;\
	leaq msg, %rsi;	\
	call print;
			
	

.text

.align 8

.globl _start

_start:
	/* stack_start is the end address of the first page */

	movq $0x200000, %rsp

	/* setup GDT */
	lgdt gdt_desc(%rip)

	/* setup IDT */
	lidt idt_desc(%rip)

setup_idt:
	leaq default_int_handler(%rip), %rdx
	movq $(0x08 << 16), %rax

	/* low 16b address */
	movw %dx, %ax

	/* 0x8e: interrupt gate, DPL = 0, type = E */
	movq $(0x8e00 << 32), %rcx
	addq %rcx, %rax

	/* high 16b (16-32) address */
	movl %edx, %ecx

	shrl $16, %ecx
	shlq $48, %rcx

	/* to %rax */
	addq %rcx, %rax

	/* high 32b (32-64) address */
	shrq $32, %rdx

	/* times of repeat */
	movq $256, %rcx

	leaq idt_table(%rip), %rdi

rep_set_idt:
	movq %rax, (%rdi)
	movq %rdx, 8(%rdi)
	addq $16, %rdi

	dec %rcx
	jnz rep_set_idt

to_init:
	/* we cannot use far-jmp in long mode. */

	/* 
	 * 'call' will push address of next code, 
	 * and 'ret' will pop it to %rip 
	 * so we can push address, 'ret' will 
	 * pop it and jump to it. 
	 */

	/* 64 bit code */

	/* return address */
	leaq init_done(%rip), %rax
	pushq %rax

	/* address will jump */
	leaq init(%rip), %rax
	pushq %rax

	retq


init_done:
	/* andq %rax, %rax, but %rax won't change. */
	testq %rax, %rax
	jnz init_panic

	movq $0, %rax

init_die:
	hlt
	jmp init_die

init_panic:
	WARNING(panic_msg(%rip))

	jmp init_die


default_int_handler:
	/* save all registers */
	pushq %rax
	pushq %rcx
	pushq %rbx
	pushq %rdx

	pushq %rbp
	pushq %rsi
	pushq %rdi

	pushq %r8
	pushq %r9
	pushq %r10
	pushq %r11

	pushq %r12
	pushq %r13
	pushq %r14
	pushq %r15

	movq %ds, %rax
	pushq %rax
	
	movq %es, %rax
	pushq %rax

	movq %fs, %rax
	pushq %rax

	movq %gs, %rax
	pushq %rax

	movq $0x10, %rax
	movq %rax, %ds
	movq %rax, %es
	movq %rax, %fs
	movq %rax, %gs

	/* put a message on screen, and die */
	WARNING(int_msg(%rip))

	popq %rax
	movq %rax, %gs
	
	popq %rax
	movq %rax, %fs

	popq %rax
	movq %rax, %es

	popq %rax
	movq %rax, %ds

	popq %r15
	popq %r14
	popq %r13
	popq %r12

	popq %r11
	popq %r10
	popq %r9
	popq %r8
	
	popq %rdi
	popq %rsi
	popq %rbp

	popq %rdx
	popq %rbx
	popq %rcx
	popq %rax


	/* goodbye! */
	sti
	iretq
	
	
.data

.align 16

.globl gdt_table

gdt_table:
	/* +0: null descriptor */
	.quad 0x0000000000000000

	/* +8: kernel code 64-bit */
	.quad 0x0020980000000000

	/* +16: kernel data 64-bit */
	.quad 0x0000920000000000

	/* +24: user code 64-bit */
	.quad 0x0020f80000000000

	/* +32: user data 64-bit */
	.quad 0x0000f20000000000

	/* tss starts at +40 (5th entry) */
	/* and gdt_table.size = 64B */

	.fill 3, 8, 0
gdt_end:

gdt_desc:
	.word gdt_end - gdt_table - 1
	.quad gdt_table
	

.globl idt_table

idt_table:
	.fill 512, 8, 0

idt_end:

idt_desc:
	.word idt_end - idt_table - 1
	.quad idt_table


.globl stack_start

stack_start:
	.quad 0x200000


.section .rodata
.align 8

message:
	.asciz "XNIX Kernel is starting..."

panic_msg:
	.asciz "panic in init: halted"

int_msg:
	.asciz "unknow interrupt or fault\n"
